﻿#pragma once

#include "GameplayEffectTypes.h"
#include "AuraAbilityTypes.generated.h"

class UGameplayEffect;
class UAbilitySystemComponent;

USTRUCT(BlueprintType)
struct FDamageEffectParams
{
	GENERATED_BODY()
	FDamageEffectParams()
	{
	}

	UPROPERTY(BlueprintReadWrite)
	TObjectPtr<UObject> WorldContextObject{nullptr};

	UPROPERTY(BlueprintReadWrite)
	TSubclassOf<UGameplayEffect> DamageGameplayEffectClass{nullptr};
	UPROPERTY(BlueprintReadWrite)
	TObjectPtr<UAbilitySystemComponent> SourceAbilitySystemComponent;
	UPROPERTY(BlueprintReadWrite)
	TObjectPtr<UAbilitySystemComponent> TargetAbilitySystemComponent;
	UPROPERTY(BlueprintReadWrite)
	float BaseDamage{0.f};
	UPROPERTY(BlueprintReadWrite)
	float AbilityLevel{1.f};
	UPROPERTY(BlueprintReadWrite)
	FGameplayTag DamageType{FGameplayTag()};
	UPROPERTY(BlueprintReadWrite)
	float DebuffChance{0.f};
	UPROPERTY(BlueprintReadWrite)
	float DebuffDamage{0.f};
	UPROPERTY(BlueprintReadWrite)
	float DebuffDuration{0.f};
	UPROPERTY(BlueprintReadWrite)
	float DebuffFrequency{0.f};
	UPROPERTY(BlueprintReadWrite)
	float DeathImpulseMagnitude{0.f};
	UPROPERTY(BlueprintReadWrite)
	FVector DeathImpulse{FVector::ZeroVector};
	UPROPERTY(BlueprintReadWrite)
	float KnockbackForceMagnitude{0.f};
	UPROPERTY(BlueprintReadWrite)
	FVector KnockbackForce{FVector::ZeroVector};
	UPROPERTY(BlueprintReadWrite)
	float KnockbackChance{0.f};
	UPROPERTY(BlueprintReadWrite)
	bool bIsRadialDamage{false};
	UPROPERTY(BlueprintReadWrite)
	float RadialDamageInnerRadius{0.f};
	UPROPERTY(BlueprintReadWrite)
	float RadialDamageOuterRadius{0.f};
	UPROPERTY(BlueprintReadWrite)
	FVector RadialDamageOrigin{FVector::ZeroVector};
};

USTRUCT(BlueprintType)
struct FAuraGameplayEffectContext : public FGameplayEffectContext
{
	GENERATED_BODY()

public:
	bool IsCriticalHit() const { return bIsCriticalHit; }
	bool IsBlockedHit() const { return bIsBlockedHit; }
	bool IsSuccessfulDebuff() const { return bIsSuccessfulDebuff; }
	float GetDebuffDamage() const { return DebuffDamage; }
	float GetDebuffDuration() const { return DebuffDuration; }
	float GetDebuffFrequence() const { return DebuffFrequency; }
	TSharedPtr<FGameplayTag> GetDamageType() const { return DamageType; }
	FVector GetDeathImpulse() const { return DeathImpulse; }
	FVector GetKnockbackImpulse() const { return KnockbackForce; }
	bool IsRadialDamage() const { return bIsRadialDamage; }
	float GetRadialDamageInnerRadius() const { return RadialDamageInnerRadius; }
	float GetRadialDamageOuterRadius() const { return RadialDamageOuterRadius; }
	FVector GetRadialDamageOrigin() const { return RadialDamageOrigin; }

	void SetIsCriticalHit(bool bInIsCriticalHit) { bIsCriticalHit = bInIsCriticalHit; }
	void SetIsBlockedHit(bool bInIsBlockedHit) { bIsBlockedHit = bInIsBlockedHit; }
	void SetIsSuccessfulDebuff(bool bInIsDebuff) { bIsSuccessfulDebuff = bInIsDebuff; }
	void SetDebuffDamage(float InDamage) { DebuffDamage = InDamage; }
	void SetDebuffDuration(float InDuration) { DebuffDuration = InDuration; }
	void SetDebuffFrequency(float InFrequency) { DebuffFrequency = InFrequency; }
	void SetDamageType(const TSharedPtr<FGameplayTag>& InDamageType) { DamageType = InDamageType; }
	void SetDeathImpulse(const FVector& InDeathImpulse) { DeathImpulse = InDeathImpulse; }
	void SetKnockbackForce(const FVector& InForce) { KnockbackForce = InForce; }
	void SetIsRadialDamage(bool bInIsRadialDamage) { bIsRadialDamage = bInIsRadialDamage; }

	void SetRadialDamageInnerRadius(float InRadialDamageInnerRadius)
	{
		RadialDamageInnerRadius = InRadialDamageInnerRadius;
	}

	void SetRadialDamageOuterRadius(float InRadialDamageOuterRadius)
	{
		RadialDamageOuterRadius = InRadialDamageOuterRadius;
	}

	void SetRadialDamageOrigin(const FVector& InRadialDamageOrigin) { RadialDamageOrigin = InRadialDamageOrigin; }

	/**
	 * GetScriptStruct: 返回用于序列化的实际结构体，子类必须覆盖它。
	 */
	virtual UScriptStruct* GetScriptStruct() const override
	{
		/**
		 * 不知道用哪个，先注释掉。
		 */
		return FAuraGameplayEffectContext::StaticStruct();
		// return FGameplayEffectContext::StaticStruct();
	}

	/**
	 * NetSerialize: 自定义序列化，子类必须覆盖它。
	 */
	/**
	 * FArchive: 可用于以字节序无关方式进行加载、保存和垃圾回收的存档的基类。
	 */
	/**
	 * UPackageMap: 为网络通信将对象和名称与索引进行相互映射。 
	 */
	virtual bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) override;
	/**
	 * 创建上下文的复制，用于在之后的修改器复制。
	 */
	virtual FAuraGameplayEffectContext* Duplicate() const
	{
		FAuraGameplayEffectContext* NewContext = new FAuraGameplayEffectContext();
		*NewContext = *this;
		if (GetHitResult())
		{
			// Does a deep copy of the hit result
			NewContext->AddHitResult(*GetHitResult(), true);
		}
		return NewContext;
	}

protected:
	UPROPERTY()
	bool bIsBlockedHit{false};
	UPROPERTY()
	bool bIsCriticalHit{false};
	UPROPERTY()
	bool bIsSuccessfulDebuff{false};
	UPROPERTY()
	float DebuffDamage{0.f};
	UPROPERTY()
	float DebuffDuration{0.f};
	UPROPERTY()
	float DebuffFrequency{0.f};

	TSharedPtr<FGameplayTag> DamageType;

	UPROPERTY()
	FVector DeathImpulse{FVector::ZeroVector};

	UPROPERTY()
	FVector KnockbackForce{FVector::ZeroVector};

	UPROPERTY()
	bool bIsRadialDamage{false};
	UPROPERTY()
	float RadialDamageInnerRadius{0.f};
	UPROPERTY()
	float RadialDamageOuterRadius{0.f};
	UPROPERTY()
	FVector RadialDamageOrigin{FVector::ZeroVector};
};

/**
 * 在UE里，TStructOpsTypeTraits是一个模板结构体，主要用于定义struct的操作特性。
 * template<>
 * struct TStructOpsTypeTraits<FGameplayEffectSpecHandle> : public TStructOpsTypeTraitsBase2<FGameplayEffectSpecHandle>
 * {
 * 	 enum
 *   {
 *	  	WithCopy = true,
 *		  WithNetSerializer = true,
 *		  WithIdenticalViaEquality = true,
 *	 };
 * };
 */
template <>
struct TStructOpsTypeTraits<FAuraGameplayEffectContext> : public TStructOpsTypeTraitsBase2<FAuraGameplayEffectContext>
{
	enum
	{
		WithNetSerializer = true,
		WithCopy = true // Necessary so that TSharedPtr<FHitResult> Data is copied around
	};
};
